extends CharacterBody2D

enum State {
	IDLE,
	RUNNING,
	JUMP,
	FALL,
	LANDING,
	WALL_SLIDING,
	WALL_JUMP,
	ATTACK,
	
}

const GROUND_STATES := [State.IDLE, State.RUNNING,State.LANDING,]
const RUN_SPEED := 150.0
const JUMP_VELOCITY := -380.0 
const FLOOR_ACCELERATION := RUN_SPEED / 0.2
const AIR_ACCELERATION := RUN_SPEED / 0.1
const  WALL_JUMP_VELOCITY := Vector2(500,-300)

var default_gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity") as float
var is_first_tick := false

@onready var graphics: Node2D = $Graphics
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var coyote_timer: Timer = $CoyoteTimer
@onready var jump_request_timer: Timer = $JumpRequestTimer
@onready var hand_checker: RayCast2D = $Graphics/HandChecker
@onready var foot_checker: RayCast2D = $Graphics/FootChecker
@onready var state_machine: StateMachine = $StateMachine

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("jump"):
		jump_request_timer.start()
	if event.is_action_released("jump"):
		jump_request_timer.stop()
		if velocity.y < JUMP_VELOCITY / 2:
			velocity.y = JUMP_VELOCITY / 2
# 跟踪当前播放的动画状态
var current_animation: String = ""
# 标志位，表示是否正在攻击
var is_attacking: bool = false
func _ready() -> void:
	# 初始化时确保动画播放器有一个默认动画
	animation_player.play("idle")

func tick_physics(state: State, delta: float) -> void:
	
	match state:
		State.IDLE:
			move(default_gravity,delta)
		State.RUNNING:
			move(default_gravity,delta)
		State.JUMP:
			move(0.0 if is_first_tick else default_gravity ,delta)
		State.FALL:
			move(default_gravity,delta)
		State.LANDING:
			stand(default_gravity,delta)
		State.WALL_SLIDING:
			move(default_gravity / 5 , delta)
			graphics.scale.x = get_wall_normal().x
		State.WALL_JUMP:
			if state_machine.state_time < 0.1:
				stand(0.0 if is_first_tick else default_gravity,delta)
				graphics.scale.x = get_wall_normal().x
			else:
				move(default_gravity ,delta)
	is_first_tick = false
	
func move(gravity:float, delta:float) -> void :
	var horizontal_input := Input.get_axis("move_left", "move_right")
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
	# 更新水平速度
	velocity.x = move_toward(velocity.x, horizontal_input * RUN_SPEED, acceleration * delta) 
	# 更新垂直速度（应用重力）
	if not is_on_floor():
		velocity.y += gravity * delta
	# 处理跳跃
	var can_jump := is_on_floor() or coyote_timer.time_left > 0 
	var should_jump := can_jump and jump_request_timer.time_left > 0
	if should_jump:
		velocity.y = JUMP_VELOCITY
		coyote_timer.stop()
		jump_request_timer.stop()
	# 处理攻击
	if Input.is_action_just_pressed("attack") and not is_attacking:
		attack()
	elif Input.is_action_just_released("attack") and is_attacking:
		is_attacking = false
		current_animation = "idle"
		animation_player.play("idle")
	# 确定当前动画状态
	var new_animation: String = ""
	if is_attacking:
		new_animation = "attack"
	elif is_on_floor():
		if is_zero_approx(horizontal_input):
			new_animation = "idle"
		else:
			new_animation = "running"
	elif velocity.y < 0:
		new_animation = "jump"
	else:
		new_animation = "fall"
	# 播放新动画（如果与当前动画不同）
	if new_animation != current_animation:
		animation_player.play(new_animation)
		current_animation = new_animation
	# 翻转精灵图像
	if not is_zero_approx(horizontal_input):
		graphics.scale.x = -1 if horizontal_input < 0 else 1
	var was_on_floor := is_on_floor()
	move_and_slide()
	if is_on_floor() != was_on_floor:
		if was_on_floor and not should_jump:
			coyote_timer.start()
		else:
			coyote_timer.stop()

func stand(gravity: float ,  delta : float ) -> void :
	var acceleration := FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
	# 更新水平速度
	velocity.x = move_toward(velocity.x, 0.0 , acceleration * delta) 
	# 更新垂直速度（应用重力）
	velocity.y += gravity * delta
	move_and_slide()

func attack() -> void:
	# 播放攻击动画
	if current_animation != "attack":
		animation_player.play("attack")
		current_animation = "attack"
		is_attacking = true

func _on_AnimationPlayer_animation_finished(anim_name: String) -> void:
	if anim_name == "attack":
		is_attacking = false
		
func  can_wall_slide() -> bool:
	return is_on_wall() and hand_checker.is_colliding() and foot_checker.is_colliding()
		
func get_next_state(state: State) -> State:
	var can_jump := is_on_floor() or coyote_timer.time_left > 0 
	var should_jump := can_jump and jump_request_timer.time_left > 0	
	if should_jump:
		return State.JUMP
	var horizontal_input := Input.get_axis("move_left", "move_right")
	var is_still := is_zero_approx(horizontal_input) and is_zero_approx(velocity.y)
	match state:
		State.IDLE:
			if not is_on_floor():
				return State.FALL
			if not is_still:
				return State.RUNNING
		State.RUNNING:
			if is_still:
				return State.IDLE
		State.JUMP:
			if velocity.y >= 0:
				return State.FALL
		State.FALL:
			if is_on_floor():
				return State.LANDING if is_still else State.RUNNING
			if can_wall_slide():
				return State.WALL_SLIDING
		State.LANDING:
			if not is_still:
				return State.RUNNING
			if not animation_player.is_playing():
				return State.IDLE
		State.WALL_SLIDING:
			if jump_request_timer.time_left > 0:
				return State.WALL_JUMP
			if is_on_floor():
				return State.IDLE
			if not is_on_wall():
				return State.FALL
		State.WALL_JUMP:
			if can_wall_slide() and not is_first_tick:
				return State.WALL_SLIDING
			if velocity.y >= 0:
				return State.FALL
	return state
	
func transition_state(from: State, to: State) -> void:
	if from not in GROUND_STATES and to in GROUND_STATES:
		coyote_timer.stop()
	
	match to:
		State.IDLE:
			animation_player.play("idle")
		State.RUNNING:
			animation_player.play("running")
		State.JUMP:
			animation_player.play("jump")
			velocity.y = JUMP_VELOCITY
			coyote_timer.stop()
			jump_request_timer.stop()
		State.FALL:
			animation_player.play("fall")
			if from in GROUND_STATES:
				coyote_timer.start()
		State.LANDING:
			animation_player.play("landing")
		State.WALL_SLIDING:
			animation_player.play("wall_sliding")	
		State.WALL_JUMP:
			animation_player.play("jump")
			velocity = WALL_JUMP_VELOCITY
			velocity.x = get_wall_normal().x
			jump_request_timer.stop()
			
	is_first_tick = true
